home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / LIB / LINE_DIS.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  19.8 KB  |  629 lines

  1. package sub_arctic.lib;
  2.  
  3. import sub_arctic.output.drawable;
  4. import sub_arctic.output.loaded_image;
  5.  
  6. import java.awt.Color;
  7. import java.awt.Point;
  8. import java.util.Vector;
  9.  
  10.  
  11. /**
  12.  * Class to display a line with optional arrowheads.  Note: the presence of
  13.  * the arrowheads makes it that case that the endpoints of the line are not
  14.  * necessarily at the corners of the bounding box (consider, for example, a 
  15.  * horizontal line).  This unfortunately makes use of the line with constraints
  16.  * still a bit problematical.
  17.  *
  18.  * @author Scott Hudson and Ian Smith 
  19.  */
  20. public class line_display extends base_interactor {
  21.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  22.  
  23.   /** 
  24.    * Interactor flag bit to indicate if line is negatively sloped: 
  25.    * 0,0 -> w,h [true] or positively sloped: 0,h -> w,0 [false].  
  26.    * This is computed dynamically as the x1,y1, x2,y2 coordinates of the 
  27.    * line are set.  (The proper value is computed as (y2 > y1)==(x2 > x1)).
  28.    */
  29.   protected static final int NEG_DIAG = FIRST_FREE_FLAG;
  30.  
  31.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  32.  
  33.   /** 
  34.    * Indicate if line is negatively sloped (i.e., to be interpreted as 
  35.    * 0,0 -> w,h rather than 0,h -> w,0).
  36.    * @return boolean indicating negative slope.
  37.    */
  38.   public boolean neg_diag() {return flag_is_set(NEG_DIAG); }
  39.  
  40.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  41.  
  42.   /** 
  43.    * Return x1 in parent's coordinate system.  Note, coordinates may be 
  44.    * switched to maintain canonical form (i.e. what was originally set as 
  45.    * x2 may be reported as x1).  Note this is the x1() position of the 
  46.    * line, the bound of the interactor object may extend a little further to
  47.    * make room for an arrowhead.
  48.    *
  49.    * @return int x1 coordinate for the line.
  50.    */
  51.   public int x1() {return x();};
  52.  
  53.   /** Return y1 in parent's coordinate system.  Note, coordinates may be 
  54.    *  switched to maintain canonical form (i.e. what was originally set as 
  55.    *  y2 may be reported as y1).  Note this is the y1() position of the 
  56.    *  line, the bound of the interactor object may extend a little further to
  57.    *  make room for an arrowhead.
  58.    *
  59.    * @return int y1 coordinate for the line.
  60.    */
  61.   public int y1() 
  62.     {
  63.       if (flag_is_set(NEG_DIAG)) 
  64.     return y();
  65.       else 
  66.     return y()+h();
  67.      }
  68.  
  69.   /** Return x2 in parent's coordinate system.  Note, coordinates may be 
  70.    *  switched to maintain canonical form (i.e. what was originally set as 
  71.    *  x2 may be reported as x1).  Note this is the x2() position of the 
  72.    *  line, the bound of the interactor object may extend a little further to
  73.    *  make room for an arrowhead.
  74.    *
  75.    * @return int x2 coordinate for the line.
  76.    */
  77.   public int x2() {return x()+w();};
  78.  
  79.   /** Return y2 in parent's coordinate system.  Note, coordinates may be 
  80.    *  switched to maintain canonical form (i.e. what was originally set as 
  81.    *  y2 may be reported as y1).  Note this is the x1() position of the 
  82.    *  line, the bound of the interactor object may extend a little further to
  83.    *  make room an arrowhead.
  84.    *
  85.    * @return int x2 coordinate for the line.
  86.    */
  87.   public int y2() 
  88.     {
  89.       if (flag_is_set(NEG_DIAG)) 
  90.     return y()+h();
  91.       else 
  92.     return y();
  93.     }
  94.  
  95.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  96.  
  97.   /** Record of line start point. */
  98.   protected Point line_start;
  99.  
  100.   /** Record of line end point. */
  101.   protected Point line_end;
  102.  
  103.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  104.  
  105.   /** 
  106.    * Set the position of the line.  Note, because of internal canonicalization,
  107.    * the line may switch endpoints and later report what is given here as x1,y1
  108.    * as x2,y2, and visa versa.
  109.    *
  110.    * @param int x1 x1 coordinate of the line (in parent's coordinate system).
  111.    * @param int y1 y1 coordinate of the line (in parent's coordinate system).
  112.    * @param int x1 x1 coordinate of the line (in parent's coordinate system).
  113.    * @param int y1 y1 coordinate of the line (in parent's coordinate system).
  114.    */
  115.   public void set_coords(int x1, int y1, int x2, int y2) 
  116. {
  117.     Vector v;
  118.     Integer x,y;
  119.     int lowest_x, highest_x, lowest_y, highest_y;
  120.  
  121.     set_flag_bit(NEG_DIAG, (y2>y1)==(x2>x1));
  122.  
  123.     line_start=new Point(x1,y1);
  124.     line_end=new Point(x2,y2);
  125.  
  126.     /* if we have arrow heads turned on, record the location of the strokes */
  127.     if (arrow_heads()>0) {
  128.       stroke1_start=new Point(x1,y1);
  129.       stroke1_end=new Point(x2,y2);
  130.  
  131.       /* now record the locations of the arrow head points */
  132.       v=stroke(stroke1_start.x,stroke1_start.y,
  133.            stroke1_end.x,stroke1_end.y,
  134.            arrow_head_length(), arrow_head_angle());
  135.       x=(Integer)v.elementAt(0);
  136.       y=(Integer)v.elementAt(1);
  137.       stroke1_pt1=new Point(x.intValue(),y.intValue());
  138.       x=(Integer)v.elementAt(2);
  139.       y=(Integer)v.elementAt(3);
  140.       stroke1_pt2=new Point(x.intValue(),y.intValue());
  141.  
  142.       /* figure out the farthest left part of this line */
  143.       lowest_x=min(x1,x2,stroke1_pt1.x,stroke1_pt2.x);
  144.  
  145.       /* figure out the farthest right part of this line */
  146.       highest_x=max(x1,x2,stroke1_pt1.x,stroke1_pt2.x);
  147.  
  148.       /* figure out the farthest up part of this line */
  149.       lowest_y=min(y1,y2,stroke1_pt1.y,stroke1_pt2.y);
  150.  
  151.       /* figure out the farthest right part of this line */
  152.       highest_y=max(y1,y2,stroke1_pt1.y,stroke1_pt2.y);
  153.  
  154.       /* is it double headed? */
  155.       if (arrow_heads()>1) {
  156.     stroke2_start=new Point(x2,y2);
  157.     stroke2_end=new Point(x1,y1);
  158.     v=stroke(stroke2_start.x,stroke2_start.y,
  159.          stroke2_end.x,stroke2_end.y,
  160.          arrow_head_length(), arrow_head_angle());
  161.     x=(Integer)v.elementAt(0);
  162.     y=(Integer)v.elementAt(1);
  163.     stroke2_pt1=new Point(x.intValue(),y.intValue());
  164.     x=(Integer)v.elementAt(2);
  165.     y=(Integer)v.elementAt(3);
  166.     stroke2_pt2=new Point(x.intValue(),y.intValue());
  167.  
  168.     /* figure out the farthest left part of this line */
  169.     lowest_x=min(lowest_x,lowest_x,stroke2_pt1.x,stroke2_pt2.x);
  170.  
  171.     /* figure out the farthest right part of this line */
  172.     highest_x=max(highest_x,highest_x,stroke2_pt1.x,stroke2_pt2.x);
  173.  
  174.     /* figure out the farthest up part of this line */
  175.     lowest_y=min(lowest_y,lowest_y,stroke2_pt1.y,stroke2_pt2.y);
  176.  
  177.     /* figure out the farthest right part of this line */
  178.     highest_y=max(highest_y,highest_y,stroke2_pt1.y,stroke2_pt2.y);
  179.       }
  180.       set_x(lowest_x);
  181.       set_y(lowest_y);
  182.       set_w(highest_x-lowest_x+1);
  183.       set_h(highest_y-lowest_y+1);
  184.       damage_self();
  185.       return;
  186.     }
  187.  
  188.     /* this is the case where there are no arrowheads */
  189.     if (x2 > x1) {
  190.       set_x(x1); set_w(x2-x1+1);
  191.     } else {
  192.       set_x(x2); set_w(x1 - x2 + 1);
  193.     }
  194.  
  195.     if (y2 > y1) {
  196.       set_y(y1); set_h(y2-y1+1);
  197.     } else {
  198.       set_y(y2); set_h(y1-y2+1);
  199.     }
  200.  
  201.     damage_self();
  202.   }
  203.  
  204.    //had:
  205.    //* @exception cannot_assign if part of the object geometry is constrained.
  206.    //* @exception general
  207.  
  208.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  209.  
  210.   /** 
  211.    * Constructor with arrowhead defaults. 
  212.    * @param int x1 x1 coordinate of the line. 
  213.    * @param int y1 y1 coordinate of the line. 
  214.    * @param int x2 x2 coordinate of the line. 
  215.    * @param int y2 y2 coordinate of the line. 
  216.    * @param int arrowheads the number of arrowheads to draw (must be 0,1, or 2).
  217.    */
  218.   public line_display(int x1, int y1, int x2, int y2, int arrowheads ) 
  219. {
  220.       /* initialize in x,y, w,h form */
  221.       super(0,0, 10,10);
  222.  
  223.       /* if arrowheads<0 or >2 there is an error */
  224.       if ((arrowheads<0)||(arrowheads>2)) {
  225.     throw new sub_arctic_error("Number of arrowheads must be 0, 1, or 2");
  226.       }
  227.  
  228.       /* note we set this first so when we set the coords we already
  229.      know to record the stroke info */
  230.       set_arrow_heads(arrowheads);
  231.  
  232.       _arrow_head_length=20;
  233.       _arrow_head_angle=25;
  234.  
  235.       /* set the real size and coords */
  236.       set_coords(x1,y1,x2,y2);
  237.  
  238.     }
  239.  
  240.    //had:
  241.    //* @exception general
  242.  
  243.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  244.  
  245.   /** 
  246.    * Full constructor.
  247.    * @param int x1 x1 coordinate of the line. 
  248.    * @param int y1 y1 coordinate of the line. 
  249.    * @param int x2 x2 coordinate of the line. 
  250.    * @param int y2 y2 coordinate of the line. 
  251.    * @param int arrowheads the number of arrowheads to draw (must be 0,1, or 2).
  252.    */
  253.   public line_display(int x1, int y1, int x2, int y2, int arrowheads,
  254.               int l, int a) 
  255. {
  256.       /* initialize in x,y, w,h form */
  257.       super(0,0, 10,10);
  258.  
  259.       /* if arrowheads<0 or >2 there is an error */
  260.       if ((arrowheads<0)||(arrowheads>2)) {
  261.     throw new sub_arctic_error("Number of arrowheads must be 0, 1, or 2");
  262.       }
  263.       /* note we set this first so when we set the coords we already
  264.      know to record the stroke info */
  265.       set_arrow_heads(arrowheads);
  266.  
  267.       _arrow_head_length=l;
  268.       _arrow_head_angle=a;
  269.  
  270.       /* set the real size and coords */
  271.       set_coords(x1,y1,x2,y2);
  272.  
  273.     }
  274.  
  275.    //had:
  276.    //* @exception general
  277.  
  278.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  279.  
  280.   // this should be replaced with new arrowhead infrastructure in drawable...
  281.  
  282.   /** Draw the image of the line */
  283.   public void draw_self_local(drawable d) {
  284.       Vector v;
  285.       Integer x,y;
  286.  
  287.       /* draw in black */
  288.       d.setColor(Color.black);
  289.       d.drawLine(x_into_local(line_start.x),
  290.          y_into_local(line_start.y),
  291.          x_into_local(line_end.x),
  292.          y_into_local(line_end.y));
  293.  
  294.       /* deal with arrow heads */
  295.       if (arrow_heads()>0) {
  296.  
  297.     /* draw the 1st part of the arrowhead to the end of the 1st stroke */
  298.     d.drawLine(x_into_local(stroke1_pt1.x),
  299.            y_into_local(stroke1_pt1.y),
  300.            x_into_local(stroke1_end.x),
  301.            y_into_local(stroke1_end.y));
  302.  
  303.     /* draw the 2nd part of the arrowhead to the end of the 1st stroke */
  304.     d.drawLine(x_into_local(stroke1_pt2.x), 
  305.            y_into_local(stroke1_pt2.y),
  306.            x_into_local(stroke1_end.x),
  307.            y_into_local(stroke1_end.y));
  308.  
  309.     /* dual heads? */
  310.     if (arrow_heads()>1) {
  311.  
  312.       /* draw the 1st part of the arrowhead to the end of the 1st stroke */
  313.       d.drawLine(x_into_local(stroke2_pt1.x),
  314.              y_into_local(stroke2_pt1.y),
  315.              x_into_local(stroke2_end.x),
  316.              y_into_local(stroke2_end.y));
  317.  
  318.       /* draw the 2nd part of the arrowhead to the end of the 1st stroke */
  319.       d.drawLine(x_into_local(stroke2_pt2.x),
  320.              y_into_local(stroke2_pt2.y),
  321.              x_into_local(stroke2_end.x),
  322.              y_into_local(stroke2_end.y));
  323.     }
  324.       }
  325.     }
  326.  
  327.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  328.  
  329.   /* data for drawing arrow heads */ 
  330.  
  331.   int _arrow_heads ;
  332.  
  333.   Point stroke1_start, stroke1_end, stroke2_start, stroke2_end;
  334.  
  335.   Point stroke1_pt1,   stroke1_pt2, stroke2_pt1,   stroke2_pt2;
  336.  
  337.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  338.  
  339.   /** 
  340.    * Set the number of arrowheads.  This must be 0, 1, or 2. 
  341.    * @param int h number of arrowheads on this line.
  342.    */
  343.   public void set_arrow_heads(int h) {
  344.     _arrow_heads=h;
  345.   }
  346.  
  347.   /**
  348.    * Number of arrowheads.
  349.    * @return int the number of arrowheads on this line.
  350.    */
  351.   public int arrow_heads() { return _arrow_heads;};
  352.  
  353.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  354.  
  355.   /**
  356.    * The length of the edges of the arrowhead lines.
  357.    */
  358.   protected int _arrow_head_length;
  359.  
  360.   /**
  361.    * The length of the edges of the arrowhead lines.
  362.    * @return int the length of the edges of the arrowhead lines.
  363.    */
  364.   public int arrow_head_length() { return _arrow_head_length;};
  365.  
  366.   /**
  367.    * Set the length of the edges of the arrowhead lines.
  368.    * @param int l the length of the edges of the arrowhead lines.
  369.    */
  370.   public void set_arrow_head_length(int l) 
  371.     _arrow_head_length=l; 
  372.     set_coords(line_start.x,line_start.y, 
  373.            line_end.x, line_end.y); /* force recompute */
  374.   }
  375.  
  376.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  377.  
  378.   /** 
  379.    * The angle (in degrees) between each of the arrowhead lines and the line
  380.    * itself.
  381.    */
  382.   protected int _arrow_head_angle;
  383.  
  384.   /** 
  385.    * The angle (in degrees) between each of the arrowhead lines and the line
  386.    * itself.
  387.    * @return int angle (in degrees) between arrowhead lines and the line itself.
  388.    */
  389.   public int arrow_head_angle() { return _arrow_head_angle;};
  390.  
  391.   /** 
  392.    * Set the angle (in degrees) between each of the arrowhead lines and the line
  393.    * itself.
  394.    * @param int a angle (in degrees) between arrowhead lines and the line 
  395.    *              itself.
  396.    */
  397.   public void set_arrow_head_angle(int a) 
  398.     _arrow_head_angle=a;
  399.     set_coords(line_start.x,line_start.y, 
  400.            line_end.x, line_end.y); /* force recompute */
  401.   };
  402.  
  403.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  404.  
  405.   /** 
  406.    * Version of min that works over 4 ints. 
  407.    * @param int i first value.
  408.    * @param int j second value.
  409.    * @param int k third value.
  410.    * @param int l third value.
  411.    * @return int the min of those 4 values.
  412.    */
  413.   static int min(int i,int j,int k,int l) {
  414.     int m=i;
  415.     if (j<m) m=j;
  416.     if (k<m) m=k;
  417.     if (l<m) m=l;
  418.     return m;
  419.   }
  420.  
  421.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  422.  
  423.   /** 
  424.    * Version of max that works over 4 ints. 
  425.    * @param int i first value.
  426.    * @param int j second value.
  427.    * @param int k third value.
  428.    * @param int l third value.
  429.    * @return int the max of those 4 values.
  430.    */
  431.   static int max(int i,int j,int k,int l) {
  432.     int m=i;
  433.     if (j>m) m=j;
  434.     if (k>m) m=k;
  435.     if (l>m) m=l;
  436.     return m;
  437.   }
  438.  
  439.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  440.  
  441.   static Vector stroke(int x1, int y1, int x2, int y2, int l, int a) {
  442.     Vector v;
  443.     double radius, theta, p1_x, p2_x, p1_y, p2_y;
  444.     double final_p1_x, final_p1_y, final_p2_x, final_p2_y;
  445.     double cos_theta, sin_theta, cart_x, cart_y;
  446.     int actual_x1, actual_y1, actual_x2, actual_y2;
  447.  
  448.     /* go to phase 1 ... return a 2 Integers transformed to have
  449.        x1,y1 as the origin and x2,y2 as the point in CARTESIAN coordinates and
  450.        the radius and angle of the x2,y2 point ...these are Floats.
  451.        The integers you input are in an AWT coordinate system. */
  452.     v=phase1(x1,y1,x2,y2);
  453.  
  454.     /* ok, got the numbers we wanted, now lets extract them */
  455.     cart_x=(double)((Integer)v.elementAt(0)).intValue();
  456.     cart_y=(double)((Integer)v.elementAt(1)).intValue();
  457.     radius=((Float)v.elementAt(2)).doubleValue();
  458.     theta=((Float)v.elementAt(3)).doubleValue();
  459.  
  460.     /* go to phase two. This returns two Point objects which are the two
  461.      * points of the arrow head, assuming this arrow was along the X axis
  462.      * pointing out and that it had the same length as the user's arrow  */
  463.     v=phase2(radius, l, a);
  464.  
  465.     /* ok, now we have the points, so lets rotate them about the correct
  466.      * angle for the actual data we have */
  467.     p1_x=((Float)v.elementAt(0)).doubleValue();
  468.     p1_y=((Float)v.elementAt(1)).doubleValue();
  469.     p2_x=((Float)v.elementAt(2)).doubleValue();
  470.     p2_y=((Float)v.elementAt(3)).doubleValue();
  471.  
  472.     /* Phase 3:
  473.        do the rotation about the actual theta for their line*/
  474.     cos_theta=Math.cos((theta/360.0)*(2.0*Math.PI));
  475.     sin_theta=Math.sin((theta/360.0)*(2.0*Math.PI));
  476.     final_p1_x=(p1_x*cos_theta) - (p1_y * sin_theta);
  477.     final_p1_y=(p1_x*sin_theta) +  (p1_y*cos_theta);
  478.     final_p2_x=(p2_x*cos_theta) - (p2_y*sin_theta);
  479.     final_p2_y=(p2_x*sin_theta) + (p2_y*cos_theta);
  480.  
  481.     /* Phase 4:
  482.        Do the conversion back to the AWT style coordinates ... don't 
  483.        forget to compensate for the original cartesian coords */
  484.     final_p1_x=((double)x1)+final_p1_x;
  485.     final_p2_x=((double)x1)+final_p2_x;
  486.     final_p1_y=((double)y1)-final_p1_y;
  487.     final_p2_y=((double)y1)-final_p2_y;
  488.  
  489.     /* convert to integers for consumption by the toolkit */
  490.     actual_x1=(int)Math.round(final_p1_x);
  491.     actual_y1=(int)Math.round(final_p1_y);
  492.     actual_x2=(int)Math.round(final_p2_x);
  493.     actual_y2=(int)Math.round(final_p2_y);
  494.  
  495.     /* make a vector for them */
  496.     v=new Vector(4);
  497.     v.addElement(new Integer(actual_x1));
  498.     v.addElement(new Integer(actual_y1));
  499.     v.addElement(new Integer(actual_x2));
  500.     v.addElement(new Integer(actual_y2));
  501.     return v;
  502.   }
  503.  
  504.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  505.     
  506.   /**
  507.    * Covert the original integer input points into 4 numbers as a vector.
  508.    * The first two are integers, which are the cartesian coordinates of
  509.    * x2, y2 if x1, y1 are the origin. The second two are are Floats which
  510.    * represent the r and \theta for x2,y2 if x1,y1 are the origin. The
  511.    * theta is in degrees, and considers the point (1,0) (cartesian) to 
  512.    * be the 0 degree point.
  513.    */
  514.   static Vector phase1(int x1, int y1, int x2, int y2) {
  515.     int cartesian_x2, cartesian_y2;
  516.     double radius, realx2, realy2, theta, degrees;
  517.     Vector v=new Vector(4);
  518.  
  519.     /* possible point */
  520.     if ((x1==x2) && (y1==y2)) {
  521.       v.addElement(new Integer(0));
  522.       v.addElement(new Integer(0));
  523.       v.addElement(new Float(0.0));
  524.       v.addElement(new Float(0.0));
  525.       return v;
  526.     }
  527.  
  528.     /* convert to cartesian, assuming x1,y1 origin */
  529.     cartesian_x2=x2-x1;
  530.     cartesian_y2=-(y2-y1);
  531.  
  532.     /* get the length of the line */
  533.     realx2=(double)cartesian_x2;
  534.     realy2=(double)cartesian_y2;
  535.     radius=Math.sqrt((realx2*realx2)+(realy2*realy2));
  536.  
  537.     /* get the theta */
  538.     theta=Math.atan(realy2/realx2);
  539.  
  540.     /* convert to degrees */
  541.     degrees=((theta/(Math.PI/2.0))*90.0);
  542.  
  543.     /* now correct to get a positive angle in degrees for the
  544.      * 2 and 4th quadrants ... also correct the angle for quad 3*/
  545.     if (realx2<0.0) {
  546.       /* quadrant 2 */
  547.       degrees+=180.0;
  548.     } else {
  549.       /* check for quad 4 */
  550.       if ((realx2>0.0) && (realy2<0.0)) {
  551.     /* is quad 4 */
  552.     degrees+=360.0;
  553.       }
  554.     }
  555.  
  556.     /* make a vector */
  557.     v=new Vector(4);
  558.     v.addElement(new Integer(cartesian_x2));
  559.     v.addElement(new Integer(cartesian_y2));
  560.     v.addElement(new Float(radius));
  561.     v.addElement(new Float(degrees));
  562.     return v; 
  563.   }
  564.  
  565.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  566.  
  567.   /**
  568.    * phase 2 computes the locations of the points given that 
  569.    * the arrow was along the x axis, pointing right. We know the
  570.    * length, so we rotate about the endpoint. 
  571.    */
  572.   static public Vector phase2(double radius, int length, int angle) {
  573.     double angle_double=(double)angle, length_double=(double)length;
  574.     double p1_x, p1_y, p2_x, p2_y;
  575.     Vector v;
  576.     double cos_angle, sin_angle;
  577.  
  578.     /**
  579.      * First rotate the points about the origin to the correct spot assuming
  580.      * the endpoint of the stroke was the origin. Since we are assuming
  581.      * a right pointing arrow, we rotate to 180+angle and 180-angle.
  582.      * Trick we assume a point (length, 0) and only do a 1/2  
  583.      * of the matrix multiply.
  584.      */
  585.     cos_angle=Math.cos(((180.0+angle_double)/360.0)*(2.0*Math.PI));
  586.     sin_angle=Math.sin(((180.0+angle_double)/360.0)*(2.0*Math.PI));
  587.  
  588.     /* rotate around the origin */
  589.     p1_x=length_double*cos_angle;
  590.     p1_y=length_double*sin_angle;
  591.  
  592.     /* symmetry: x is the same, and y is just the opposite sign */
  593.     p2_x=p1_x;
  594.     p2_y=-p1_y;
  595.  
  596.     /* Ok, now shift this to the right by an amount equal to the radius */
  597.     p1_x+=radius;
  598.     p2_x+=radius;
  599.  
  600.     /* get the result ready */
  601.     v=new Vector(4);
  602.     v.addElement(new Float(p1_x));
  603.     v.addElement(new Float(p1_y));
  604.     v.addElement(new Float(p2_x));
  605.     v.addElement(new Float(p2_y));
  606.     return v;
  607.   }
  608.  
  609.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  610. }
  611. /*=========================== COPYRIGHT NOTICE ===========================
  612.  
  613. This file is part of the subArctic user interface toolkit.
  614.  
  615. Copyright (c) 1996 Scott Hudson and Ian Smith
  616. All rights reserved.
  617.  
  618. The subArctic system is freely available for most uses under the terms
  619. and conditions described in 
  620.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  621. and appearing in full in the lib/interactor.java source file.
  622.  
  623. The current release and additional information about this software can be 
  624. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  625.  
  626. ========================================================================*/
  627.